#include "../ocl/program.h"
#include "../ocl/driver.h"

const char ocl_options[] = 
#if MDS_DOUBLE == 1
"-DReal=double -DReal2=double2 -DReal4=double4 -DReal8=double8 -DOCL_KHR_FP64"
#else
"-DReal=float -DReal2=float2 -DReal4=float4 -DReal8=float8"
#endif
;

int ocl_program_load_sources( ocl_program *program, ocl_context *context, const ocl_code *sources )
{
	static const char fn[] = "[ocl_program_create_with_sources]";
	assert( program );
	assert( context );
	assert( sources );
	memset( program, 0, sizeof(ocl_program) );
	ocl_errno = CL_SUCCESS;
	{
		const size_t count = sources->count;
		if( ocl_logfile ) fprintf( ocl_logfile, "%s #sources = %u\n", fn, (unsigned) count );
		
		if(  count <= 0 )
		{
			errno = ERANGE;
			return -1;
		}
		else 
		{
			/* allocate memory for program input */
			const size_t src_size = sizeof(char   *) * count;
			const size_t len_size  = sizeof(size_t)   * count;
			const size_t all_size  = src_size + len_size;
			char        *array     = MDS_CALLOC_AS(char,all_size);
			if( ocl_logfile ) fprintf( ocl_logfile, "%s CALLOC(%u)\n", fn, (unsigned) all_size );
			if( !array )
			{
				return -1;
			}
			else {
				/* fill memory informations */
				size_t           i;
				char           **src     = (char **) & array[0];
				size_t          *len     = (size_t *) &array[src_size];
				const ocl_chunk *chunk   = sources->head;
				if( ocl_logfile ) fprintf( ocl_logfile, "%s Preparing sources\n", fn );
				for( i=0; i < count; ++i, chunk=chunk->next )
				{
					src[i] = chunk->data;
					len[i] = chunk->size;
				}
				
				/* OpenCL call */
				if( ocl_logfile ) fprintf( ocl_logfile, "%s Creating...\n", fn);
				program->handle = clCreateProgramWithSource( context->handle, count, (const char **)src, len, & ocl_errno);
				
				/* free memory information */
				
				MDS_FREE_AS(char,array,all_size);
				if( ocl_logfile ) fprintf( ocl_logfile, "%s FREE(%u)\n", fn, (unsigned) all_size );
				if( CL_SUCCESS != ocl_errno )
					return -1;
				
				program->context = context;
				return 0;
			}
			
		}
		
		
	}
	
}

void ocl_program_quit( ocl_program *program )
{
	
	if( program )
	{
		if( program->handle )
			clReleaseProgram( program->handle );
		memset( program, 0, sizeof(ocl_program) );
	}
}

int  ocl_program_compile( ocl_program *program, const char *options )
{
	static const char fn[] = "[ocl_program_compile]";
	assert( program );
	assert( program->context );
	assert( program->context->num_devices > 0 );
	assert( program->context->DEVICES != NULL );
	if( ocl_logfile ) { fprintf( ocl_logfile, "%s compile with '%s'\n", fn, options ? options : "no options" ); fflush(ocl_logfile); }
	ocl_errno = clBuildProgram(program->handle, 
							   program->context->num_devices, 
							   program->context->DEVICES,
							   options,
							   NULL, NULL);
	if( ocl_errno != CL_SUCCESS )
		return -1;
	
	return 0;
}


int  ocl_program_build_logs( const ocl_program *program, ocl_code *build_logs)
{
	register cl_uint i;
	assert( program );
	assert( program->context );
	assert( build_logs );
	
	for( i=0; i < program->context->num_devices; ++i )
	{
		const cl_device_id device = program->context->DEVICES[i];
		size_t             length = 0;
		ocl_chunk         *chunk  = NULL;
		
		
		/** fetch the length */
		if( CL_SUCCESS != ( ocl_errno = clGetProgramBuildInfo(program->handle,
															  device,
															  CL_PROGRAM_BUILD_LOG, 
															  0, 
															  NULL, 
															  &length)))
		{
			return -1;
		}
		
		/** create the chunk */
		chunk = ocl_chunk_create( length+1 );
		if( !chunk ) return -1;
		
		
		/** read the log */
		if( CL_SUCCESS != ( ocl_errno = clGetProgramBuildInfo(program->handle,
															  device,
															  CL_PROGRAM_BUILD_LOG, 
															  length, 
															  chunk->data, 
															  NULL)))
		{
			ocl_chunk_delete(chunk);
			return -1;
		}
		
		/** done */
		chunk->data[ length ] = 0;
		ocl_code_add( build_logs, chunk );
		
		
	}
	
	
	
	
	return 0;
}


int  ocl_program_display_build_logs( const ocl_program *program, FILE *fp )
{
	int         res        = 0;
	ocl_code    build_logs = OCL_CODE_INIT;
	ocl_driver *OpenCL     = ocl_driver_load();
	assert( program );
	assert( program->context );
	assert( fp );
	
	if( NULL == OpenCL || ocl_program_build_logs( program, &build_logs) < 0 ) 
		return -1;
	else
	{
		ocl_chunk          *chunk     = build_logs.head;
		const cl_device_id *device_id = program->context->DEVICES;
		assert( build_logs.count == program->context->num_devices );
		
		fprintf( fp, "<Build Logs>\n");
		while( chunk )
		{
			ocl_device *D = ocl_driver_device( *device_id );
			fprintf( fp, "/** %s **/\n", D->NAME );
			fprintf( fp, "%s\n", chunk->data );
			chunk = chunk->next;
			++device_id;
		}
		fprintf( fp, "</Build Logs>\n");
	}
	
	
	
	ocl_code_quit( &build_logs );
	return res;
	
}



